package roborally;

import java.awt.IllegalComponentStateException;
import java.util.List;
import java.util.concurrent.ExecutionException;

import roborally.Exceptions.IllegalDestinationException;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;

/**
 * A class of placeables, that extends Terminateable, it has a coordinate, board and type.
 * 
 * @invar |Coordinate.isValidXCoordinate(board,coordinate.getX());
 * @invar |Coordinate.isValidYCoordinate(board,coordinate.getY());
 * 
 * @version  2.90
 * @author   Michiel Ruelens & Dieter Naten
 */
public abstract class Placeable extends Terminateable {

	public Coordinate coordinate;
	public Board board;
	public PlaceableType placeableType;
	
	/**
	 * A constructor for the class of placeables.
	 */
	@Raw
	public Placeable(){
	}
	
	/**
	 * Method to put a placeable on the given board at a given x- and y-value.
	 * 
	 * @param 	board
	 * 			The board that the placeable is being put on.
	 * @param 	x
	 *			The x-coordinate that the placeable is getting.
	 * @param 	y
	 * 			The y-coordinate that the placeable is getting.
	 * @effect  | setBoard(board)
     * @effect  | setCoordinate(x,y)
     * @effect  | board.addPlaceable(this)
     * @throws 	IllegalComponentStateException
     * 			Thrown when the placeable being put is terminated.
     * 		 	| this.isTerminated()==true
	 */
	@Raw
	public void putPlaceable(Board board, long x, long y) throws  IllegalComponentStateException {
		if(this.isTerminated()){
			System.out.println("The placeable you tried to put on the board is terminated.");
			throw new IllegalComponentStateException();
		}
		else{
			try{
				setBoard(board);
				setCoordinate(x,y);
				board.addPlaceable(this);
			}
			catch(IllegalDestinationException exc){ // THROWN IN SETCOORDINATE(x,y)
				System.out.println("The destinated coordinate is not a valid one. " +
						"You tried to move placeable(Wall): " + exc.getPlaceable() +
						", to: "+ exc.getDestination()+
						", of coordinate-type: "+ exc.getType());
			}
		}
	}
	
	/**
	 * This method will return the coordinate object of this Placeable.
	 */
	@Basic
	public Coordinate getCoordinate(){
		return this.coordinate;
	}
	
	/**
	 * This method will return the current X-coordinate of the Placeable.
	 */
	@Basic
	public long getX() {
		return coordinate.getX();
	}
	
	/**
	 * This method will return the current Y-coordinate of the Placeable.
	 */
	@Basic
	public long getY() {
		return coordinate.getY();
	}
	
	/**
	 * Set the x- and y-value of the coordinate of this Placeable to the given x- and y-value.
	 * 
	 * @param 	x
	 * 			The desired x-coordinate.
	 * @param 	y
	 * 			The desired y-coordinate.
	 * @post 	| (new this).getX() == x
	 * @post 	| (new this).getY() == y
	 * @throws 	IllegalComponentStateException
     * 			Thrown when the placeable is terminated.
     * 		 	| this.isTerminated()==true
	 * @throws 	IllegalDestinationException
	 * 		 	| if(!Coordinate.isValidXCoordinate(board,x) || !Coordinate.isValidYCoordinate(board,y))
	 * @throws 	IllegalArgumentException
	 * 	     	| if(board.hasWallAtPosition(x,y)
	 */
	public void setCoordinate(long x, long y) throws IllegalDestinationException, IllegalArgumentException, IllegalComponentStateException {
		if(this.isTerminated()){
			System.out.println("That placeable is terminated so you cannot set it's coordinates.");
			throw new IllegalComponentStateException();
		}
		else{
			if(!Coordinate.isValidXCoordinate(board,x)){
				throw new IllegalDestinationException(this,x,"x");
			}
			else if(!Coordinate.isValidYCoordinate(board,y)){
				throw new IllegalDestinationException(this,y,"y");
			}
			else if(board.hasWallAtPosition(x, y)){
				throw new IllegalArgumentException();
			}
			else{
				this.coordinate = Coordinate.getCoordinate(x,y);
			}
		}
	}
		
	/**
	 * Return the board this placeable is currently on.
	 */
	@Basic
	public Board getBoard() {
		return board;
	}
	
	/**
	 * Set the board of this placeable to the given board.
	 * 
	 * @param 	board
	 * 			The board that we want to give to this placeable.
	 * @post 	| (new this).getBoard() == board
	 * @throws 	IllegalComponentStateException
     * 			Thrown when the placeable is terminated.
     * 		 	| this.isTerminated()==true
     * @throws	IllegalArgumentException
     * 			Thrown when the setBoard method is called with an argument that is not null, while this placeable already has a board.
     * 			| this.getBoard()!=null
	 */
	public void setBoard(Board board) throws IllegalComponentStateException{
		if(this.isTerminated()){
			System.out.println("That placeable is terminated so you cannot set it's board.");
			throw new IllegalComponentStateException();
		}
		else if(this.getBoard()==null){
			this.board = board;
		}
		else if(board==null){
			this.board = board;
		}
		else if(this.getBoard()!=null){
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Return the type of this placeable.
	 */
	@Basic
	public PlaceableType getPlaceableType() {
		return placeableType;
	}
	
	/**
	 * Set the type of this placeable to the given type.
	 * 
	 * @param 	type
	 * 			The type that we want to give to this placeable.
	 * @post 	| (new this).getType() == type
	 * @throws 	IllegalComponentStateException
     * 			Thrown when the placeable is terminated.
     * 		 	| this.isTerminated()==true
	 */
	public void setPlaceableType(PlaceableType type) throws IllegalComponentStateException{
		if(this.isTerminated()){
			System.out.println("That placeable is terminated so you cannot set it's type.");
			throw new IllegalComponentStateException();
		}
		else{
			this.placeableType = type;
		}
	}
	
	/**
	 * Terminates this placeable.
	 * 
	 * @effect	| if(!this.isTerminated() && this.getBoard()!=null) {
	 * 			|	this.getBoard().removePlaceable(this)	
	 * 			| 	this.setBoard(null) }
	 * @effect	| if(!this.isTerminated()) {super.terminate()}
	 */
	@Override
	public void terminate(){
		if(!this.isTerminated()){
			if(this.getBoard()!=null){
				Board board = this.getBoard();
				board.removePlaceable(this);
				this.setBoard(null);
			}
			super.terminate();
		}
		else{
			System.out.println("That placeable has already been terminated.");
		}
	}
	
	/**
	 * Method needed in subclasses, when the placeable is hit.
	 */
	public void hit(){
		// THIS METHOD IS JUST HERE, NEEDED IN SUPERCLASS
	}
}